{
 "cells": [
  {
   "cell_type": "code",
   "execution_count": 41,
   "metadata": {
    "colab": {},
    "colab_type": "code",
    "id": "Vc1tLT83PQMd"
   },
   "outputs": [],
   "source": [
    "# ML_in_Finance-G-learning-wealth-management\n",
    "# Author: Igor Halperin and Matthew Dixon\n",
    "# Version: 1.0 (12.8.2019)\n",
    "# License: MIT\n",
    "# Email: ighalp@gmail.com\n",
    "# Notes: tested on Mac OS X with Python 3.6.9 and the following packages:\n",
    "# pytorch=1.5.0, numpy=1.18.1, scipy=1.4.1, matplotlib=3.1.3\n",
    "# Citation: Please cite the following reference if this notebook is used for research purposes:\n",
    "# Dixon M.F., I. Halperin and P. Bilokon, Machine Learning in Finance: From Theory to Practice, Springer Graduate Textbook Series, 2020. "
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "colab_type": "text",
    "id": "uXdeHDmEPQM6"
   },
   "source": [
    "## G-learning for wealth optimization\n",
    "\n",
    "The purpose of this notebook is to demonstrate the use of G-learning with quadratic rewards for optimization of a defined contribution retirement plan."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 2,
   "metadata": {
    "colab": {},
    "colab_type": "code",
    "id": "vOoE16hWPQNC"
   },
   "outputs": [],
   "source": [
    "import numpy as np\n",
    "import matplotlib.pyplot as plt \n",
    "%matplotlib inline\n",
    "\n",
    "import time\n",
    "from scipy.optimize import minimize\n",
    "import torch\n",
    "\n",
    "import torch.optim as optim\n",
    "\n",
    "import warnings\n",
    "warnings.filterwarnings('ignore')"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 4,
   "metadata": {
    "colab": {
     "base_uri": "https://localhost:8080/",
     "height": 34
    },
    "colab_type": "code",
    "id": "PHyhUTpJ4nl1",
    "outputId": "18b1c78b-d6c6-4453-9755-65390364ab53"
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "env: KMP_DUPLICATE_LIB_OK=TRUE\n"
     ]
    }
   ],
   "source": [
    "%env KMP_DUPLICATE_LIB_OK=TRUE"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 5,
   "metadata": {
    "colab": {},
    "colab_type": "code",
    "id": "S0yKeMU9PQNa"
   },
   "outputs": [],
   "source": [
    "# set the device\n",
    "device = 'cuda' if torch.cuda.is_available() else 'cpu'"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 7,
   "metadata": {
    "colab": {},
    "colab_type": "code",
    "id": "qLivr9yuPQON"
   },
   "outputs": [],
   "source": [
    "# Define the G-learning portfolio optimization class\n",
    "class G_learning_portfolio_opt:\n",
    "    \n",
    "    def __init__(self, \n",
    "                 num_steps,\n",
    "                 params,\n",
    "                 beta,\n",
    "                 benchmark_portf,\n",
    "                 gamma, \n",
    "                 num_risky_assets,\n",
    "                 riskfree_rate,\n",
    "                 exp_returns, # array of shape num_steps x num_stocks\n",
    "                 Sigma_r,     # covariance matrix of returns of risky assets\n",
    "                 init_x_vals, # array of initial asset position values (num_risky_assets + 1)\n",
    "                 use_for_WM = True): # use for wealth management tasks\n",
    "\n",
    "                \n",
    "        self.num_steps = num_steps\n",
    "        self.num_assets = num_risky_assets + 1 \n",
    "        \n",
    "        self.lambd = torch.tensor(params[0], requires_grad=False, dtype=torch.float64)\n",
    "        self.Omega_mat = params[1] * torch.eye(self.num_assets,dtype=torch.float64)\n",
    "        self.eta = torch.tensor(params[2], requires_grad=False, dtype=torch.float64)\n",
    "        self.rho = torch.tensor(params[3], requires_grad=False, dtype=torch.float64)\n",
    "        self.beta = torch.tensor(beta, requires_grad=False, dtype=torch.float64)\n",
    "        \n",
    "        self.gamma = gamma\n",
    "        self.use_for_WM = use_for_WM\n",
    "        \n",
    "        self.num_risky_assets = num_risky_assets\n",
    "        self.r_f = riskfree_rate\n",
    "        \n",
    "        \n",
    "        assert exp_returns.shape[0] == self.num_steps\n",
    "        assert Sigma_r.shape[0] == Sigma_r.shape[1]\n",
    "        assert Sigma_r.shape[0] == num_risky_assets # self.num_assets\n",
    "        \n",
    "        self.Sigma_r_np = Sigma_r # array of shape num_stocks x num_stocks\n",
    "        \n",
    "        self.reg_mat = 1e-3*torch.eye(self.num_assets, dtype=torch.float64)\n",
    "        \n",
    "        # arrays of returns for all assets including the risk-free asset\n",
    "        # array of shape num_steps x (num_stocks + 1) \n",
    "        self.exp_returns_np = np.hstack((self.r_f * np.ones(self.num_steps).reshape((-1,1)), exp_returns))\n",
    "                                      \n",
    "        # make block-matrix Sigma_r_tilde with Sigma_r_tilde[0,0] = 0, and equity correlation matrix inside\n",
    "        self.Sigma_r_tilde_np = np.zeros((self.num_assets, self.num_assets))\n",
    "        self.Sigma_r_tilde_np[1:,1:] = self.Sigma_r_np\n",
    "            \n",
    "        # make Torch tensors  \n",
    "        self.exp_returns = torch.tensor(self.exp_returns_np,requires_grad=False, dtype=torch.float64)\n",
    "        self.Sigma_r = torch.tensor(Sigma_r,requires_grad=False, dtype=torch.float64)\n",
    "        self.Sigma_r_tilde = torch.tensor(self.Sigma_r_tilde_np,requires_grad=False, dtype=torch.float64)\n",
    "        \n",
    "        self.benchmark_portf = torch.tensor(benchmark_portf, requires_grad=False, dtype=torch.float64)\n",
    "        \n",
    "        # asset holding values for all times. Initialize with initial values, \n",
    "        # values for the future times will be expected values \n",
    "        self.x_vals_np = np.zeros((self.num_steps, self.num_assets))\n",
    "        self.x_vals_np[0,:] = init_x_vals \n",
    "        \n",
    "        # Torch tensor\n",
    "        self.x_vals = torch.tensor(self.x_vals_np)\n",
    "                \n",
    "        # allocate memory for coefficients of R-, F- and G-functions        \n",
    "        self.F_xx = torch.zeros(self.num_steps, self.num_assets, self.num_assets, dtype=torch.float64,\n",
    "                                requires_grad=True)\n",
    "        self.F_x = torch.zeros(self.num_steps, self.num_assets, dtype=torch.float64,\n",
    "                               requires_grad=True)\n",
    "        self.F_0 = torch.zeros(self.num_steps,dtype=torch.float64,requires_grad=True)\n",
    "        \n",
    "        self.Q_xx = torch.zeros(self.num_steps, self.num_assets, self.num_assets,dtype=torch.float64,\n",
    "                                requires_grad=True)\n",
    "        self.Q_uu = torch.zeros(self.num_steps, self.num_assets, self.num_assets,dtype=torch.float64,\n",
    "                                requires_grad=True)\n",
    "        self.Q_ux = torch.zeros(self.num_steps, self.num_assets, self.num_assets,dtype=torch.float64,\n",
    "                                requires_grad=True)\n",
    "        self.Q_x = torch.zeros(self.num_steps, self.num_assets,dtype=torch.float64,requires_grad=True)\n",
    "        self.Q_u = torch.zeros(self.num_steps, self.num_assets,dtype=torch.float64,requires_grad=True)\n",
    "        self.Q_0 = torch.zeros(self.num_steps,dtype=torch.float64,requires_grad=True)\n",
    "        \n",
    "        self.R_xx = torch.zeros(self.num_steps, self.num_assets, self.num_assets,dtype=torch.float64,\n",
    "                                requires_grad=True)\n",
    "        self.R_uu = torch.zeros(self.num_steps, self.num_assets, self.num_assets,dtype=torch.float64,\n",
    "                                requires_grad=True)\n",
    "        self.R_ux = torch.zeros(self.num_steps, self.num_assets, self.num_assets,dtype=torch.float64,\n",
    "                                requires_grad=True)\n",
    "        self.R_x = torch.zeros(self.num_steps, self.num_assets,dtype=torch.float64,requires_grad=True)\n",
    "        self.R_u = torch.zeros(self.num_steps, self.num_assets,dtype=torch.float64,requires_grad=True)\n",
    "        self.R_0 = torch.zeros(self.num_steps,dtype=torch.float64,requires_grad=True)\n",
    "\n",
    "        \n",
    "        self.reset_prior_policy()\n",
    "        \n",
    "        # the list of adjustable model parameters:\n",
    "        self.model_params = [self.lambd, self.beta, self.Omega_mat, self.eta]         \n",
    "        \n",
    "        # expected cash installment for all steps\n",
    "        self.expected_c_t = torch.zeros(self.num_steps,dtype=torch.float64)\n",
    "        \n",
    "        # realized values of the target portfolio\n",
    "        self.realized_target_portf = np.zeros(self.num_steps,dtype=np.float64)\n",
    "        \n",
    "        # expected portfolio values for all times\n",
    "        self.expected_portf_val = torch.zeros(self.num_steps,dtype=torch.float64)\n",
    "        \n",
    "        # the first value is the sum of initial position values\n",
    "        self.expected_portf_val[0] = self.x_vals[0,:].sum()\n",
    "\n",
    "    def reset_prior_policy(self):\n",
    "        # initialize time-dependent parameters of prior policy \n",
    "        self.u_bar_prior = torch.zeros(self.num_steps,self.num_assets,requires_grad=False,\n",
    "                                       dtype=torch.float64)\n",
    "        self.v_bar_prior =  torch.zeros(self.num_steps, self.num_assets, self.num_assets,requires_grad=False,\n",
    "                                        dtype=torch.float64)\n",
    "        self.Sigma_prior =  torch.zeros(self.num_steps, self.num_assets, self.num_assets,requires_grad=False,\n",
    "                                        dtype=torch.float64)\n",
    "        self.Sigma_prior_inv = torch.zeros(self.num_steps, self.num_assets, self.num_assets,requires_grad=False,\n",
    "                                        dtype=torch.float64)\n",
    "        \n",
    "        # make each time elements of v_bar_prior and Sigma_prior proportional to the unit matrix\n",
    "        for t in range(self.num_steps):\n",
    "            self.v_bar_prior[t,:,:] = 0.1 * torch.eye(self.num_assets).clone()\n",
    "            self.Sigma_prior[t,:,:] = 0.1 * torch.eye(self.num_assets).clone()\n",
    "            self.Sigma_prior_inv[t,:,:] = 10.0 * torch.eye(self.num_assets).clone() # np.linalg.inv(self.Sigma_prior[t,:,:])\n",
    "    \n",
    "    def reward_fun(self, t, x_vals, u_vals, exp_rets, lambd, Sigma_hat):\n",
    "        \"\"\"\n",
    "        The reward function \n",
    "        \"\"\"\n",
    "        x_plus = x_vals + u_vals\n",
    "        \n",
    "        p_hat = self.rho.clone() * self.benchmark_portf[t] + (1-self.rho.clone())*self.eta.clone()*x_vals.sum()\n",
    "        \n",
    "        aux_1 = - self.lambd.clone() * p_hat**2         \n",
    "        aux_2 = - u_vals.sum()   \n",
    "        aux_3 = 2*self.lambd.clone() * p_hat * x_plus.dot(torch.ones(num_assets) + exp_rets)\n",
    "        aux_4 = - self.lambd.clone() * x_plus.mm(Sigma_hat.mv(x_plus))\n",
    "        aux_5 = - u_vals.mm(self.Omega_mat.clone().mv(u_vals))\n",
    "        \n",
    "        return aux_1 + aux_2 + aux_3 + aux_4 + aux_5  \n",
    "    \n",
    "    def compute_reward_fun(self):\n",
    "        \"\"\"\n",
    "        Compute coefficients R_xx, R_ux, etc. for all steps\n",
    "        \"\"\"\n",
    "        for t in range(0, self.num_steps):\n",
    "            \n",
    "            one_plus_exp_ret = torch.ones(self.num_assets,dtype=torch.float64) + self.exp_returns[t,:]\n",
    "            benchmark_portf = self.benchmark_portf[t]\n",
    "            Sigma_hat = self.Sigma_r_tilde + torch.ger(one_plus_exp_ret, one_plus_exp_ret)\n",
    "            \n",
    "            one_plus_exp_ret_by_one = torch.ger(one_plus_exp_ret,torch.ones(self.num_assets,dtype=torch.float64))\n",
    "            one_plus_exp_ret_by_one_T = one_plus_exp_ret_by_one.t()     \n",
    "            one_one_T_mat = torch.ones(self.num_assets,self.num_assets)\n",
    "            \n",
    "            self.R_xx[t,:,:] = (-self.lambd.clone()*(self.eta.clone()**2)*(self.rho.clone()**2)*one_one_T_mat\n",
    "                                 + 2*self.lambd.clone()*self.eta.clone()*self.rho.clone()*one_plus_exp_ret_by_one\n",
    "                                 - self.lambd.clone()*Sigma_hat)\n",
    "            \n",
    "            self.R_ux[t,:,:] = (2*self.lambd.clone()*self.eta.clone()*self.rho.clone()*one_plus_exp_ret_by_one\n",
    "                                 - 2*self.lambd.clone()*Sigma_hat)\n",
    "            \n",
    "            self.R_uu[t,:,:] = - self.lambd.clone() * Sigma_hat - self.Omega_mat.clone()\n",
    "            \n",
    "            self.R_x[t,:] =  (-2*self.lambd.clone()*self.eta.clone()*self.rho.clone()*(1-self.rho.clone())*benchmark_portf *\n",
    "                                 torch.ones(self.num_assets,dtype=torch.float64)\n",
    "                                 + 2*self.lambd.clone()*(1-self.rho.clone())*benchmark_portf * one_plus_exp_ret)\n",
    "            \n",
    "            self.R_u[t,:] = (2*self.lambd.clone()*(1-self.rho.clone())*benchmark_portf * one_plus_exp_ret\n",
    "                             - torch.ones(self.num_assets,dtype=torch.float64))\n",
    "            \n",
    "            self.R_0[t] = - self.lambd.clone()*((1-self.rho.clone())**2) * (benchmark_portf**2)\n",
    "                \n",
    "         \n",
    "    def project_cash_injections(self):\n",
    "        \"\"\"\n",
    "        Compute the expected values of future asset positions, and the expected cash injection for future steps,\n",
    "        as well as realized values of the target portfolio\n",
    "        \"\"\"\n",
    "           \n",
    "        # this assumes that the policy is trained\n",
    "        for t in range(1, self.num_steps):  # the initial value is fixed \n",
    "            # increment the previous x_t\n",
    "            delta_x_t = self.u_bar_prior[t,:] + self.v_bar_prior[t,:,:].mv(self.x_vals[t-1,:])\n",
    "            self.x_vals[t,:] = self.x_vals[t-1,:] + delta_x_t\n",
    "            \n",
    "            # grow using the expected return\n",
    "            self.x_vals[t,:] = (torch.ones(self.num_assets)+ self.exp_returns[t,:])*self.x_vals[t,:]\n",
    "            \n",
    "            # compute c_t\n",
    "            self.expected_c_t[t] = delta_x_t.sum().data # detach().numpy()\n",
    "            \n",
    "            # expected portfolio value for this step\n",
    "            self.expected_portf_val[t] = self.x_vals[t,:].sum().data # .detach().numpy()\n",
    "                                                                                      \n",
    "    def set_terminal_conditions(self):\n",
    "        \"\"\"\n",
    "        set the terminal condition for the F-function\n",
    "        \"\"\"\n",
    "        \n",
    "        # the auxiliary quantity to perform matrix calculations\n",
    "        one_plus_exp_ret = torch.ones(self.num_assets,dtype=torch.float64) + self.exp_returns[-1,:]\n",
    "        \n",
    "        # Compute the reward function for all steps (only the last step is needed for this functions, while \n",
    "        # values for other time steps will be used in other functions)\n",
    "        self.compute_reward_fun()\n",
    "        \n",
    "        if self.use_for_WM:\n",
    "            Sigma_hat = self.Sigma_r_tilde + torch.ger(one_plus_exp_ret, one_plus_exp_ret)\n",
    "            Sigma_hat_inv = torch.inverse(Sigma_hat + self.reg_mat)\n",
    "            \n",
    "            Sigma_tilde = Sigma_hat + (1/self.lambd)*self.Omega_mat.clone()\n",
    "            Sigma_tilde_inv = torch.inverse(Sigma_tilde + self.reg_mat)\n",
    "            \n",
    "            Sigma_hat_sigma_tilde = Sigma_hat.mm(Sigma_tilde)\n",
    "            Sigma_tilde_inv_sig_hat = Sigma_tilde_inv.mm(Sigma_hat)\n",
    "            Sigma_tilde_sigma_hat = Sigma_tilde.mm(Sigma_hat)\n",
    "            \n",
    "            Sigma_hat_Sigma_tilde_inv = Sigma_hat.mm(Sigma_tilde_inv)\n",
    "            Sigma_3_plus_omega = self.lambd*Sigma_tilde_inv.mm(Sigma_hat_Sigma_tilde_inv) + self.Omega_mat.clone()    \n",
    "                             \n",
    "            one_plus_exp_ret_by_one = torch.ger(one_plus_exp_ret,torch.ones(self.num_assets,dtype=torch.float64))\n",
    "            one_plus_exp_ret_by_one_T = one_plus_exp_ret_by_one.t()     \n",
    "            one_one_T_mat = torch.ones(self.num_assets,self.num_assets)\n",
    "            \n",
    "            Sigma_tilde_inv_t_R_ux = Sigma_tilde_inv.t().mm(self.R_ux[-1,:,:].clone())\n",
    "            Sigma_tilde_inv_t_R_uu = Sigma_tilde_inv.t().mm(self.R_uu[-1,:,:].clone())\n",
    "            Sigma_tilde_inv_t_R_u = Sigma_tilde_inv.t().mv(self.R_u[-1,:].clone())\n",
    "            \n",
    "            Sigma_tilde_inv_R_u = Sigma_tilde_inv.mv(self.R_u[-1,:].clone())\n",
    "            Sigma_tilde_inv_R_ux = Sigma_tilde_inv.mm(self.R_ux[-1,:,:].clone())\n",
    "            Sigma_tilde_inv_t_R_uu = Sigma_tilde_inv.mm(self.R_uu[-1,:,:].clone())\n",
    "            \n",
    "            # though the action at the last step is deterministic, we can feed \n",
    "            # parameters of the prior with these values                     \n",
    "              \n",
    "            self.u_bar_prior[-1,:]   = (1/(2 * self.lambd.clone()))* Sigma_tilde_inv.clone().mv(self.R_u[-1,:].clone())\n",
    "            self.v_bar_prior[-1,:,:] = (1/(2 * self.lambd.clone()))* Sigma_tilde_inv.clone().mm(self.R_ux[-1,:,:].clone())    \n",
    "                \n",
    "            # First compute the coefficients of the reward function F at the last step:        \n",
    "            # F_xx                 \n",
    "            self.F_xx[-1,:,:] = (self.R_xx[-1,:,:].clone()\n",
    "                                 + (1/(2*self.lambd.clone()))* self.R_ux[-1,:,:].clone().t().mm(Sigma_tilde_inv_t_R_ux)\n",
    "                                 + (1/(4*self.lambd.clone()**2))* self.R_ux[-1,:,:].clone().t().mm(\n",
    "                                      Sigma_tilde_inv_t_R_uu.clone().mm(Sigma_tilde_inv.clone().mm(self.R_ux[-1,:,:].clone())))\n",
    "                                )\n",
    "            \n",
    "            # F_x                    \n",
    "            self.F_x[-1,:] = (self.R_x[-1,:].clone()\n",
    "                                 + (1/(self.lambd.clone()))* self.R_ux[-1,:,:].clone().t().mv(Sigma_tilde_inv_t_R_u.clone())\n",
    "                                 + (1/(2*self.lambd.clone()**2))* self.R_ux[-1,:,:].clone().t().mv(\n",
    "                                      Sigma_tilde_inv_t_R_uu.clone().mv(Sigma_tilde_inv_R_u.clone()))\n",
    "                            )\n",
    "            \n",
    "            # F_0   \n",
    "            self.F_0[-1] = (self.R_0[-1].clone() \n",
    "                            +  (1/(2*self.lambd.clone()))* self.R_u[-1,:].clone().dot(Sigma_tilde_inv_R_u.clone())\n",
    "                            + (1/(4*self.lambd.clone()**2))* self.R_u[-1,:].clone().dot(\n",
    "                                Sigma_tilde_inv_t_R_uu.clone().mv(Sigma_tilde_inv_R_u.clone()))\n",
    "                           )\n",
    "            \n",
    "            # for the Q-function at the last step:\n",
    "            self.Q_xx[-1,:,:] = self.R_xx[-1,:,:].clone()\n",
    "            self.Q_ux[-1,:,:] = self.R_ux[-1,:,:].clone()\n",
    "            self.Q_uu[-1,:,:] = self.R_uu[-1,:,:].clone()\n",
    "            self.Q_u[-1,:] = self.R_u[-1,:].clone()\n",
    "            self.Q_x[-1,:] = self.R_x[-1,:].clone()\n",
    "            self.Q_0[-1] = self.R_0[-1].clone()\n",
    "            \n",
    "    def G_learning(self, err_tol, max_iter):\n",
    "        \"\"\"\n",
    "        find the optimal policy for the time dependent policy\n",
    "        \n",
    "        \"\"\"   \n",
    "        print('Doing G-learning, it may take a few seconds...')\n",
    "        \n",
    "        # set terminal conditions\n",
    "        self.set_terminal_conditions()\n",
    "        \n",
    "        # allocate iteration numbers for all steps\n",
    "        self.iter_counts = np.zeros(self.num_steps)\n",
    "        \n",
    "        # iterate over time steps backward\n",
    "        for t in range(self.num_steps-2,-1,-1):\n",
    "            self.step_G_learning(t, err_tol, max_iter)\n",
    "            \n",
    "    def step_G_learning(self, t, err_tol, max_iter):\n",
    "        \"\"\"\n",
    "        Perform one step of backward iteration for G-learning self-consistent equations\n",
    "        This should start from step t = num_steps - 2 (i.e. from a step that is before the last one)\n",
    "        \"\"\"\n",
    "            \n",
    "        # make matrix Sigma_hat_t        \n",
    "        one_plus_exp_ret = torch.ones(self.num_assets,dtype=torch.float64) + self.exp_returns[t,:]\n",
    "        Sigma_hat_t = self.Sigma_r_tilde + torch.ger(one_plus_exp_ret, one_plus_exp_ret)\n",
    "        \n",
    "        # matrix A_t = diag(1 + r_bar_t)\n",
    "        A_t = torch.diag(torch.ones(self.num_assets,dtype=torch.float64) + self.exp_returns[t,:])\n",
    "                    \n",
    "        # update parameters of Q_function using next-step F-function values\n",
    "        self.update_Q_params(t, A_t,Sigma_hat_t)\n",
    "             \n",
    "        # iterate between policy evaluation and policy improvement  \n",
    "        while self.iter_counts[t] < max_iter:\n",
    "                \n",
    "            curr_u_bar_prior = self.u_bar_prior[t,:].clone()  \n",
    "            curr_v_bar_prior = self.v_bar_prior[t,:,:].clone()     \n",
    "                \n",
    "            # compute parameters of F-function for this step from parameters of Q-function\n",
    "            self.update_F_params(t) \n",
    "              \n",
    "            # Policy iteration step: update parameters of the prior policy distribution\n",
    "            # with given Q- and F-function parameters\n",
    "            self.update_policy_params(t)    \n",
    "            \n",
    "            # difference between the current value of u_bar_prior and the previous one\n",
    "            err_u_bar = torch.sum((curr_u_bar_prior - self.u_bar_prior[t,:])**2)\n",
    "            \n",
    "            # divide by num_assets in err_v_bar to get both errors on a comparable scale\n",
    "            err_v_bar = (1/self.num_assets)*torch.sum((curr_v_bar_prior - self.v_bar_prior[t,:,:])**2)\n",
    "            \n",
    "            # choose the difference from the previous iteration as the maximum of the two errors\n",
    "            tol = torch.max(err_u_bar, err_v_bar)  # tol = 0.5*(err_u_bar + err_v_bar)\n",
    "            \n",
    "            self.iter_counts[t] += 1\n",
    "            # Repeat the calculation of Q- and F-values\n",
    "            if tol <= err_tol:\n",
    "                break\n",
    "                \n",
    "    def update_Q_params(self,t, A_t,Sigma_hat_t):\n",
    "        \"\"\"\n",
    "        update the current (time-t) parameters of Q-function from (t+1)-parameters of F-function\n",
    "        \"\"\" \n",
    "                \n",
    "        ones = torch.ones(self.num_assets,dtype=torch.float64)    \n",
    "        one_plus_exp_ret = torch.ones(self.num_assets,dtype=torch.float64) + self.exp_returns[t,:]\n",
    "    \n",
    "        self.Q_xx[t,:,:] = (self.R_xx[t,:,:].clone() \n",
    "                            + self.gamma *( (A_t.clone().mm(self.F_xx[t+1,:,:].clone())).mm(A_t.clone())  \n",
    "                                           + self.Sigma_r_tilde.clone() * self.F_xx[t+1,:,:].clone() ) )\n",
    "\n",
    "\n",
    "        self.Q_ux[t,:,:] = (self.R_ux[t,:,:].clone() \n",
    "                            + 2 * self.gamma *( (A_t.clone().mm(self.F_xx[t+1,:,:].clone())).mm(A_t.clone())  \n",
    "                                           + self.Sigma_r_tilde.clone() * self.F_xx[t+1,:,:].clone() ) \n",
    "                           )\n",
    "    \n",
    "        self.Q_uu[t,:,:] = (self.R_uu[t,:,:].clone()  \n",
    "                            + self.gamma *( (A_t.clone().mm(self.F_xx[t+1,:,:].clone())).mm(A_t.clone())  \n",
    "                                           + self.Sigma_r_tilde.clone() * self.F_xx[t+1,:,:].clone() )\n",
    "                            - self.Omega_mat.clone()\n",
    "                           )\n",
    "\n",
    "\n",
    "        self.Q_x[t,:] = self.R_x[t,:].clone() + self.gamma * A_t.clone().mv(self.F_x[t+1,:].clone()) \n",
    "        self.Q_u[t,:] = self.R_u[t,:].clone() + self.gamma * A_t.clone().mv(self.F_x[t+1,:].clone())\n",
    "        self.Q_0[t]   = self.R_0[t].clone() + self.gamma * self.F_0[t+1].clone()\n",
    "\n",
    "    def update_F_params(self,t):\n",
    "        \"\"\"\n",
    "        update the current (time-t) parameters of F-function from t-parameters of G-function\n",
    "        This is a policy evaluation step: it uses the current estimations of the mean parameters of the policy\n",
    "        \n",
    "        \"\"\"\n",
    "        \n",
    "        # produce auxiliary parameters U_t, W_t, Sigma_tilde_t\n",
    "        U_t = (self.beta.clone() * self.Q_ux[t,:,:].clone() \n",
    "               + self.Sigma_prior_inv[t,:,:].clone().mm(self.v_bar_prior[t,:,:].clone()))\n",
    "        W_t = (self.beta.clone() * self.Q_u[t,:].clone() \n",
    "               +  self.Sigma_prior_inv[t,:,:].clone().mv(self.u_bar_prior[t,:]).clone())\n",
    "        Sigma_p_bar =  self.Sigma_prior_inv[t,:,:].clone() - 2 * self.beta.clone() * self.Q_uu[t,:,:].clone()\n",
    "        Sigma_p_bar_inv = torch.inverse(Sigma_p_bar + self.reg_mat)\n",
    "        \n",
    "        # update parameters of F-function\n",
    "        self.F_xx[t,:,:] = self.Q_xx[t,:,:].clone() + (1/(2*self.beta.clone()))*(U_t.t().mm(Sigma_p_bar_inv.clone().mm(U_t))\n",
    "                                    - self.v_bar_prior[t,:,:].clone().t().mm(\n",
    "                                        self.Sigma_prior_inv[t,:,:].clone().mm(self.v_bar_prior[t,:,:].clone())))\n",
    "        \n",
    "        \n",
    "        self.F_x[t,:] = self.Q_x[t,:].clone() + (1/self.beta.clone())*(U_t.mv(Sigma_p_bar_inv.clone().mv(W_t))\n",
    "                                    - self.v_bar_prior[t,:,:].clone().mv(\n",
    "                                        self.Sigma_prior_inv[t,:,:].clone().mv(self.u_bar_prior[t,:].clone())))\n",
    "        \n",
    "        \n",
    "        self.F_0[t] = self.Q_0[t].clone() + ( (1/(2*self.beta.clone()))*(W_t.dot(Sigma_p_bar_inv.clone().mv(W_t))\n",
    "                                    - self.u_bar_prior[t,:].clone().dot(\n",
    "                                        self.Sigma_prior_inv[t,:,:].clone().mv(self.u_bar_prior[t,:].clone())))\n",
    "                                    - (1/(2*self.beta.clone())) * (torch.log(torch.det(self.Sigma_prior[t,:,:].clone()+\n",
    "                                                                              self.reg_mat))\n",
    "                                                       - torch.log(torch.det(Sigma_p_bar_inv.clone() + self.reg_mat))) )\n",
    "\n",
    "    def update_policy_params(self,t):\n",
    "        \"\"\"\n",
    "        update parameters of the Gaussian policy using current coefficients of the F- and G-functions\n",
    "        \"\"\"\n",
    "        \n",
    "        new_Sigma_prior_inv = self.Sigma_prior_inv[t,:,:].clone() - 2 * self.beta.clone() * self.Q_uu[t,:,:].clone()\n",
    "\n",
    "        Sigma_prior_new = torch.inverse(new_Sigma_prior_inv + self.reg_mat)\n",
    "        \n",
    "        # update parameters using the previous value of Sigma_prior_inv\n",
    "        self.u_bar_prior[t,:] = Sigma_prior_new.mv(self.Sigma_prior_inv[t,:,:].clone().mv(self.u_bar_prior[t,:].clone())\n",
    "                                              + self.beta.clone() * self.Q_u[t,:].clone())\n",
    "        \n",
    "        \n",
    "        self.v_bar_prior[t,:,:] = Sigma_prior_new.clone().mm(self.Sigma_prior_inv[t,:,:].clone().mm(self.v_bar_prior[t,:,:].clone())\n",
    "                                              + self.beta.clone() * self.Q_ux[t,:,:].clone())\n",
    "        \n",
    "        # and then assign the new inverse covariance for the prior for the next iteration\n",
    "        self.Sigma_prior[t,:,:] = Sigma_prior_new.clone()\n",
    "        self.Sigma_prior_inv[t,:,:] = new_Sigma_prior_inv.clone()\n",
    "        \n",
    "        # also assign the same values for the previous time step\n",
    "        if t > 0:\n",
    "            self.Sigma_prior[t-1,:,:] = self.Sigma_prior[t,:,:].clone()\n",
    "            self.u_bar_prior[t-1,:] = self.u_bar_prior[t,:].clone()\n",
    "            self.v_bar_prior[t-1,:,:] = self.v_bar_prior[t,:,:].clone()\n",
    "            \n",
    "    def trajs_to_torch_tensors(self,trajs):\n",
    "        \"\"\"\n",
    "        Convert data from a list of lists into Torch tensors\n",
    "        \"\"\"\n",
    "        num_trajs = len(trajs)\n",
    "        \n",
    "        self.data_xvals = torch.zeros(num_trajs,self.num_steps,self.num_assets,dtype=torch.float64)\n",
    "        self.data_uvals = torch.zeros(num_trajs,self.num_steps,self.num_assets,dtype=torch.float64)\n",
    "            \n",
    "        for n in range(num_trajs):\n",
    "            for t in range(self.num_steps):\n",
    "                self.data_xvals[n,t,:] = torch.tensor(trajs[n][t][0],dtype=torch.float64).clone()\n",
    "                self.data_uvals[n,t,:] = torch.tensor(trajs[n][t][1],dtype=torch.float64).clone()\n",
    "                \n",
    "    def compute_reward_on_traj(self,\n",
    "                              t,\n",
    "                              x_t, u_t):\n",
    "        \"\"\"\n",
    "        Given time t and corresponding values of vectors x_t, u_t, compute the total reward for this step\n",
    "        \"\"\"\n",
    "        \n",
    "        aux_xx = x_t.dot(self.R_xx[t,:,:].clone().mv(x_t))\n",
    "        aux_ux = u_t.dot(self.R_ux[t,:,:].clone().mv(x_t))\n",
    "        aux_uu = u_t.dot(self.R_uu[t,:,:].clone().mv(u_t))\n",
    "        aux_x = x_t.dot(self.R_x[t,:].clone())\n",
    "        aux_u = u_t.dot(self.R_u[t,:].clone())\n",
    "        aux_0 = self.R_0[t].clone()\n",
    "        \n",
    "        return aux_xx + aux_ux + aux_uu + aux_x + aux_u + aux_0\n",
    "    \n",
    "    def compute_G_fun_on_traj(self,\n",
    "                              t,\n",
    "                              x_t, u_t):\n",
    "        \"\"\"\n",
    "        Given time t and corresponding values of vectors x_t, u_t, compute the total reward for this step\n",
    "        \"\"\"\n",
    "        \n",
    "        aux_xx = x_t.dot(self.Q_xx[t,:,:].clone().mv(x_t))\n",
    "        aux_ux = u_t.dot(self.Q_ux[t,:,:].clone().mv(x_t))\n",
    "        aux_uu = u_t.dot(self.Q_uu[t,:,:].clone().mv(u_t))\n",
    "        aux_x = x_t.dot(self.Q_x[t,:].clone())\n",
    "        aux_u = u_t.dot(self.Q_u[t,:].clone())\n",
    "        aux_0 = self.Q_0[t].clone()\n",
    "        \n",
    "        return aux_xx + aux_ux + aux_uu + aux_x + aux_u + aux_0\n",
    "    \n",
    "    def compute_F_fun_on_traj(self,\n",
    "                              t,\n",
    "                              x_t):\n",
    "        \"\"\"\n",
    "        Given time t and corresponding values of vectors x_t, u_t, compute the total reward for this step\n",
    "        \"\"\"\n",
    "        \n",
    "        aux_xx = x_t.dot(self.F_xx[t,:,:].clone().mv(x_t))\n",
    "        aux_x = x_t.dot(self.F_x[t,:].clone())\n",
    "        aux_0 = self.F_0[t].clone()\n",
    "        \n",
    "        return aux_xx + aux_x + aux_0\n",
    "                 \n",
    "    def MaxEntIRL(self,\n",
    "                  trajs,\n",
    "                  learning_rate,\n",
    "                  err_tol, max_iter):\n",
    "        \n",
    "        \"\"\"\n",
    "        Estimate parameters of the reward function using MaxEnt IRL.\n",
    "        Inputs:\n",
    "        \n",
    "        trajs - a list of trajectories. Each trajectory is a list of state-action pairs, stored as a tuple.\n",
    "                We assume each trajectory has the same length\n",
    "        \"\"\"\n",
    "        \n",
    "        # omega is a tunable parameter that determines the cost matrix self.Omega_mat\n",
    "        omega_init = 15.0\n",
    "        self.omega = torch.tensor(omega_init, requires_grad=True, dtype=torch.float64)\n",
    "        \n",
    "        beta_init = 50 # Beta is fixed and not a learned parameter.\n",
    "        self.beta = torch.tensor(beta_init, requires_grad=True, dtype=torch.float64)\n",
    "        \n",
    "        reward_params =  [self.lambd, self.eta, self.rho, self.omega, self.beta]\n",
    "        \n",
    "        print(\"Omega mat...\")\n",
    "        self.Omega_mat = self.omega * torch.eye(self.num_assets,dtype=torch.float64)\n",
    "        print(\"g learning...\")\n",
    "        self.reset_prior_policy()\n",
    "        self.G_learning(err_tol, max_iter)\n",
    "        print(\"intialize optimizer...\")\n",
    "        optimizer = optim.Adam(reward_params, lr=learning_rate)\n",
    "        print(\"zero grad...\")\n",
    "        optimizer.zero_grad()\n",
    "        \n",
    "        num_trajs = len(trajs)\n",
    "        print(\"trajs_to_torch_tensors...\")\n",
    "        \n",
    "        # fill in Torch tensors for the trajectory data\n",
    "        self.trajs_to_torch_tensors(trajs)\n",
    "        print(\"constructing zero tensors...\")   \n",
    "        self.realized_rewards = torch.zeros(num_trajs,self.num_steps,dtype=torch.float64,requires_grad=True)\n",
    "        self.realized_cum_rewards = torch.zeros(num_trajs, dtype=torch.float64, requires_grad=True)\n",
    "        print(\"constructing zero tensors...\")  \n",
    "        self.realized_G_fun = torch.zeros(num_trajs,self.num_steps,dtype=torch.float64, requires_grad=True)\n",
    "        self.realized_F_fun = torch.zeros(num_trajs,self.num_steps,dtype=torch.float64, requires_grad=True)\n",
    "        print(\"constructing zero tensors...\")  \n",
    "        self.realized_G_fun_cum = torch.zeros(num_trajs,dtype=torch.float64, requires_grad=True)\n",
    "        self.realized_F_fun_cum = torch.zeros(num_trajs,dtype=torch.float64, requires_grad=True)\n",
    "        print(\"done...\")  \n",
    "        \n",
    "        num_iter_IRL = 3\n",
    "        \n",
    "        for i in range(num_iter_IRL):\n",
    "            print('GIRL iteration = ', i)\n",
    "    \n",
    "            self.Omega_mat = self.omega * torch.eye(self.num_assets,dtype=torch.float64)\n",
    "    \n",
    "            for n in range(101):\n",
    "                if n%100==0:\n",
    "                    print(n)\n",
    "                for t in range(self.num_steps):\n",
    "                    \n",
    "                    # compute rewards obtained at each step for each trajectory\n",
    "                    # given the model parameters\n",
    "                    self.realized_rewards[n,t] = self.compute_reward_on_traj(t,\n",
    "                                                                self.data_xvals[n,t,:],\n",
    "                                                                self.data_uvals[n,t,:])\n",
    "                                                                \n",
    "            \n",
    "                    # compute the log-likelihood by looping over trajectories\n",
    "                    self.realized_G_fun[n,t] = self.compute_G_fun_on_traj(t,\n",
    "                                                                self.data_xvals[n,t,:],\n",
    "                                                                self.data_uvals[n,t,:])\n",
    "                \n",
    "                \n",
    "                    self.realized_F_fun[n,t] = self.compute_F_fun_on_traj(t,\n",
    "                                                                self.data_xvals[n,t,:])\n",
    "                \n",
    "\n",
    "                self.realized_cum_rewards[n] = self.realized_rewards[n,:].sum().clone()\n",
    "                self.realized_G_fun_cum[n] = self.realized_G_fun[n,:].sum().clone()\n",
    "                self.realized_F_fun_cum[n] = self.realized_F_fun[n,:].sum().clone()\n",
    "            \n",
    "            # the negative log-likelihood will not include terms ~ Sigma_p as we do not optimize over its value\n",
    "            loss = - self.beta.clone()*(self.realized_G_fun_cum.sum().clone() - self.realized_F_fun_cum.sum().clone())\n",
    "        \n",
    "            optimizer.zero_grad()\n",
    "        \n",
    "            loss.backward() \n",
    "        \n",
    "            optimizer.step()\n",
    "        \n",
    "            print('Iteration = ', i)\n",
    "            print('Loss = ', loss.detach().numpy())\n",
    "        \n",
    "           \n",
    "        print('Done optimizing reward parameters')          "
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "colab_type": "text",
    "id": "N_8XeDlpPQOV"
   },
   "source": [
    "## Simulate portfolio data"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "colab_type": "text",
    "id": "Gy2u0fkyPQOX"
   },
   "source": [
    "### Simulate the market factor under a lognormal distribution with a fixed drift and vol"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 42,
   "metadata": {
    "colab": {},
    "colab_type": "code",
    "id": "F25fZ-s9PQOb"
   },
   "outputs": [],
   "source": [
    "mu_market = 0.05\n",
    "vol_market = 0.25\n",
    "init_market_val = 100.0\n",
    "\n",
    "r_rf = 0.02  # risk-free rate - the first asset will be cash\n",
    "\n",
    "num_steps = 10 # number of steps for planning horizon\n",
    "dt = 0.25 # quarterly time steps\n",
    "\n",
    "num_risky_assets = 99 # 100\n",
    "\n",
    "returns_market = np.zeros(num_steps)\n",
    "market_vals = np.zeros(num_steps)\n",
    "market_vals[0] = 100.0  # initial value\n",
    "\n",
    "\n",
    "        \n",
    "for t in range(1,num_steps):\n",
    "\n",
    "        rand_norm = np.random.randn()\n",
    "        \n",
    "        # use log-returns of market as 'returns_market'\n",
    "        returns_market[t] = mu_market * dt + vol_market * np.sqrt(dt) * rand_norm\n",
    "        \n",
    "        market_vals[t] = market_vals[t-1] * np.exp((mu_market - 0.5*vol_market**2)*dt + \n",
    "                                                         vol_market*np.sqrt(dt)*rand_norm)\n",
    "        "
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "colab_type": "text",
    "id": "sUUBRbwbPQO1"
   },
   "source": [
    "### Simulate market betas and idiosyncratic alphas within pre-defined ranges"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 9,
   "metadata": {
    "colab": {
     "base_uri": "https://localhost:8080/",
     "height": 86
    },
    "colab_type": "code",
    "id": "qraR7fRVPQO4",
    "outputId": "dfb260b8-fa17-4a70-a12d-86a4c2964e3c"
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "[0.432154   0.69429681 0.65291633 0.20281246 0.6297545  0.45951082\n",
      " 0.81936096 0.60434031 0.63572725 0.27054397]\n",
      "[0.08447497 0.13529556 0.03017776 0.04019423 0.0133307  0.06760831\n",
      " 0.13574017 0.0626343  0.10870257 0.08363703]\n"
     ]
    }
   ],
   "source": [
    "beta_min = 0.05\n",
    "beta_max = 0.85\n",
    "beta_vals = np.random.uniform(low=beta_min, high=beta_max, size=num_risky_assets)\n",
    "\n",
    "alpha_min = - 0.05\n",
    "alpha_max = 0.15\n",
    "alpha_vals = np.random.uniform(low=alpha_min, high=alpha_max, size=num_risky_assets)\n",
    "\n",
    "print(beta_vals[0:10])\n",
    "print(alpha_vals[0:10])"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "colab_type": "text",
    "id": "DNFzG5dgPQPB"
   },
   "source": [
    "### Simulate time-dependent expected returns"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 10,
   "metadata": {
    "colab": {},
    "colab_type": "code",
    "id": "a36GRMAlPQPD"
   },
   "outputs": [],
   "source": [
    "# Time-independent expected returns would be equal to alpha + beta * expected_market_return \n",
    "# Make them time-dependent (and correlated with actual returns) as alpha + beta * oracle_market_returns\n",
    "\n",
    "oracle_coeff = 0.2\n",
    "mu_vec = mu_market * np.ones(num_steps)\n",
    "oracle_market_returns = mu_vec * dt + oracle_coeff*(returns_market - mu_vec)\n",
    "\n",
    "expected_risky_returns = np.zeros((num_steps, num_risky_assets))\n",
    "\n",
    "for t in range(num_steps):\n",
    "    expected_risky_returns[t,:] = alpha_vals * dt + beta_vals * oracle_market_returns[t]"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "colab_type": "text",
    "id": "DDN_R3znPQPK"
   },
   "source": [
    "### Initial values of all assets "
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 11,
   "metadata": {
    "colab": {},
    "colab_type": "code",
    "id": "YneTj6IBPQPM"
   },
   "outputs": [],
   "source": [
    "val_min = 20.0\n",
    "val_max = 120.0\n",
    "\n",
    "init_risky_asset_vals = np.random.uniform(low=val_min, high=val_max, size=num_risky_assets)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "colab_type": "text",
    "id": "nqd7WIxbPQPS"
   },
   "source": [
    "### Simulate realized returns and asset prices"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 43,
   "metadata": {
    "colab": {},
    "colab_type": "code",
    "id": "tSL2TU1IPQPW"
   },
   "outputs": [],
   "source": [
    "# Generate realized returns and realized asset values by simulating from a one-factor model \n",
    "# with time-dependent expected returns\n",
    "\n",
    "risky_asset_returns = np.zeros((num_steps, num_risky_assets))\n",
    "risky_asset_vals = np.zeros((num_steps, num_risky_assets))\n",
    "\n",
    "idiosync_vol =  0.05 # vol_market  \n",
    "\n",
    "for t in range(num_steps):\n",
    "    \n",
    "    rand_norm = np.random.randn(num_risky_assets)\n",
    "        \n",
    "    # asset returns are simulated from a one-factor model\n",
    "    risky_asset_returns[t,:] = (expected_risky_returns[t,:] + beta_vals * (returns_market[t] - mu_market * dt) \n",
    "                         + idiosync_vol * np.sqrt(1 - beta_vals**2) * np.sqrt(dt) * rand_norm)\n",
    "        \n",
    "    # asset values\n",
    "    if t == 0:\n",
    "        risky_asset_vals[t,:] = init_risky_asset_vals\n",
    "    else:\n",
    "        risky_asset_vals[t] = risky_asset_vals[t-1] * (1 + risky_asset_returns[t,:])\n",
    "   "
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 44,
   "metadata": {
    "colab": {
     "base_uri": "https://localhost:8080/",
     "height": 295
    },
    "colab_type": "code",
    "id": "YvHvV7B6PQPh",
    "outputId": "3421ed28-a65f-4362-d166-990d22dbf1fd"
   },
   "outputs": [
    {
     "data": {
      "image/png": "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4T9PwD333FPoXP8pKSk0z/lDLgA7duzgvffeK3Q9hhE1pKfDkiVHCkGFCq6VYC0CIz+o6sG8OwVh6NChNG3aNIAWHZ2j5fopqBAcbS4Dw4holiyBzMwjhQCce6iICUHRDB99+OHAN92SkuDNN49aJCUlhY4dO9K2bVumT5/Oww8/zODBg0lPT6dBgwYMHz6c8uXL88ILL/Djjz+SlpbGueeey/vvv39EUriLLrqIAQMGsG7dOp599lnAZTQ9cOAAK1euzHPeglmzZnHXXXdRtmzZY2bzzDlPwMSJE+nfvz8jR44kPT2dzp078/zzz9OnTx+WL19OUlIS7dq144orrjgs/fVDDz1Eq1at6Nat2xFzGQwePJjWrVszadIkduzYwbBhwzj//PMLcSEMIwQke2fMbZZLJuPERPj2W9i79/D+gyjGWgQBZvHixdx+++2MHz+eYcOG8euvvzJ79mxatWrF66+/Drgb54wZM/B4PKSlpR28oebGVVddxdy5c5k7dy6JiYk8/vjjZGRk0LNnT0aNGnXwxv/0008DcOeddzJw4MAjMpjmxfTp0xkxYgQTJ05k3LhxLF26lL///pu5c+cya9YspkyZwiuvvEKDBg2YO3cu/fv3P2advrkMunTpArjWxt9//82bb77J888/ny+7DCOseDxu3EDjxkduS0x04aPz54feriBRNFsEx3hyDyZ169bl7LPPZvTo0SxYsODgnAMHDhzgnHPOAWDSpEm89tpr7Nu3j23bttGsWTOuvPLKo9b72muvUaZMGR588EE8Hk+u8xbs3LmTHTt2HEwqd9ttt/HTTz8dtV7/eQLGjRvHuHHjOO200wCXNnvp0qXUqVPnuM6Bby4DH9deey0AZ5xxBikpKcdVl2GEBY8HGjUCv3TpB/GPHDr77NDaFSSKphCEEV/efVWlXbt2fPHFF4dt379/Pz169GDmzJnUrl2bvn37HpG7PycTJkzg66+/ZsqUKQfrzm3egh07dhR43gFfvU8++ST333//YWVy3rz95x3wfae86oRDcw/YvANG1ODxwOmn576tTh2oVKlIRQ6ZayhInH322UydOpVly5YBboKaJUuWHLxpVqtWjT179hwzb/+qVavo0aMHI0eOpEyZMgB5zltQqVIlKlaseHCKyc8+++y4bL7sssv48MMP2bNnDwBr165l06ZNR8w7ULdu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0CDcFhlG4PF4oFSpyPl9+0cORZAQ2MjiWOWpp9xTyZQp7mmpXz83AtMwihLJyS7RXFxcuC1xNGgAZctGXOSQCUEsMn06vPsuPPQQLFoEV14JzzzjnlYmTgy3dYYROMKdYygncXHQsmXEdRibEISYVVv3cs+Imdzx4d/8tWJr6A04cADuuceFsb34Ipx8MowcCT/95IbhX3IJ3HorbNwYetsMI5Ds2AGpqZElBHAockhzRtmHDxOCEJGZlc2QKcu57M0p/LViK8nrdnHTkD/pOuTP0ArCK684l9CgQVChwqH1HTrA/PmuZfD1124U5tyZUwoAACAASURBVHvvuY4tw4hGkpPdeyQKwY4dsHp1uC05iAlBCFiwbhed35vGS2MXcd4p1Rn/6IX8/kRbnunUlKWb9nDTkD+5+YM/+XvltuAasnChawV06QJXXHHk9jJl4IUXYN48aNUKHnzQdSjPnh1cuwwjGERaxJAP39wEEeQeMiEIIvszsuj/yyKueucP1u9M452bT+OD28/gpIqlKVMyjrvPq3dQEJZs3MON708PniBkZ8O990L58vDWW0cv27gxjB8Pn33mnlrOPBN69YKdOwNvl2EEi+Rk93uvUyfclhxOixZutjQTgqLP3yu3cfnA33l30nKuOe1kxj9yIZ1a1sI7ZuIg/oLw7ytOPSgItwz9kxkpARSE99+HqVPh9dehRo1jlxeBm292nckPPADvvOOiL776KqJ8m4aRJx6PSy0huWW4CSPly7vooQiKHBKNwj91q1atdObMyJy6YPf+DF79eRGf/rma+MplePnaFpzfMP+5kdIOZPHZX6sY/NsKtuxJp80pVXn40kacmVCl4EalprpJMVq3hnHjCvbHmDHDCcKsWdCunYs6atiw4DYZRrCpUQOuugqGDg23JUdyww0wZw4sWxbSw4rIrNzS/VuLIIBMWLiR9m9M4bO/VnP3efUY98gFxyUC4FoI95xf/2ALYfGG3dwweDq3Dv2LmQVpIag6X39mJgweXPCnozPPhL/+grffdu8tWsDzz0fcUHnDAFzO/82bI69/wEdiIixf7gZ2RgAmBAFgy550en4xh7tHzOSE0iX43wPn8kynppQtWfCB24cE4WL+fcWpLNqwi+sLIgjffAM//OA6gQs7ujIu7tDYg86doW9fJwjjxxeuXsMINJHaUezDN8J43rzw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\n",
      "text/plain": [
       "<Figure size 432x288 with 1 Axes>"
      ]
     },
     "metadata": {
      "needs_background": "light"
     },
     "output_type": "display_data"
    }
   ],
   "source": [
    "# Compare the realized and expected returns\n",
    "# Note that they should appear correlated.\n",
    "\n",
    "# pick a random asset ID to show\n",
    "asset_idx =  4 \n",
    "\n",
    "plt.plot(expected_risky_returns[:,asset_idx],label='expected_return')\n",
    "plt.plot(risky_asset_returns[:,asset_idx],label='realized_return',color='r')\n",
    "plt.legend()\n",
    "plt.xlabel('Time Steps')\n",
    "plt.title('Realized returns vs expected returns')\n",
    "plt.show()"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "colab_type": "text",
    "id": "WqAdjcoFPQPp"
   },
   "source": [
    "### Compute the empirical correlation matrix using realized returns"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 15,
   "metadata": {
    "colab": {
     "base_uri": "https://localhost:8080/",
     "height": 34
    },
    "colab_type": "code",
    "id": "sl5JQ3_8PQPs",
    "outputId": "5f93d784-2e83-4a2c-bf2e-23bd42eb5fb8"
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "(99, 99)\n"
     ]
    }
   ],
   "source": [
    "cov_mat_r = np.cov(risky_asset_returns.T) \n",
    "\n",
    "print(cov_mat_r.shape)\n",
    "\n",
    "D, v = np.linalg.eigh(cov_mat_r)\n",
    "\n",
    "eigenvals = D[::-1]  # put them in a descended order"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 16,
   "metadata": {
    "colab": {
     "base_uri": "https://localhost:8080/",
     "height": 69
    },
    "colab_type": "code",
    "id": "H-HzoeHOPQP0",
    "outputId": "96c11580-037d-430d-fe77-5e54635ea519"
   },
   "outputs": [
    {
     "data": {
      "text/plain": [
       "array([7.49421159e-01, 7.63706897e-03, 5.29617203e-03, 4.98433340e-03,\n",
       "       4.30600119e-03, 3.85091510e-03, 3.59793869e-03, 3.31467914e-03,\n",
       "       2.98900025e-03, 1.05366683e-16])"
      ]
     },
     "execution_count": 16,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "# eigenvalues: the largest eigenvalue is the market factor \n",
    "eigenvals[0:10]"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 17,
   "metadata": {
    "colab": {
     "base_uri": "https://localhost:8080/",
     "height": 156
    },
    "colab_type": "code",
    "id": "hFC61Ci94nnd",
    "outputId": "fc1d8cde-9ada-4e39-f6e6-3fe3b55559e0"
   },
   "outputs": [
    {
     "data": {
      "text/plain": [
       "tensor([[ 41.9146,   3.0379,   1.1985,  ...,   6.1452,  -6.1712,   1.9932],\n",
       "        [  3.0379,   7.1035,  -7.5198,  ...,   4.9974,   0.6266,   6.6633],\n",
       "        [  1.1985,  -7.5198,  14.3182,  ...,  -5.7285,  -3.4099, -10.6943],\n",
       "        ...,\n",
       "        [  6.1452,   4.9974,  -5.7285,  ...,  13.1193,  -0.8697,  -4.2431],\n",
       "        [ -6.1712,   0.6266,  -3.4099,  ...,  -0.8697,   7.8170,  -3.0512],\n",
       "        [  1.9932,   6.6633, -10.6943,  ...,  -4.2431,  -3.0512,  32.3427]],\n",
       "       dtype=torch.float64)"
      ]
     },
     "execution_count": 17,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "cov_mat_torch = torch.tensor(cov_mat_r)\n",
    "\n",
    "torch.pinverse(cov_mat_torch)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "colab_type": "text",
    "id": "ohSC6oDePQP5"
   },
   "source": [
    "### Add a riskless bond as one more asset"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 18,
   "metadata": {
    "colab": {},
    "colab_type": "code",
    "id": "F7nwaM28PQP9"
   },
   "outputs": [],
   "source": [
    "num_assets = num_risky_assets + 1\n",
    "\n",
    "bond_val = 100.0\n",
    "\n",
    "# add the bond to initial assets\n",
    "init_asset_vals = np.hstack((np.array([bond_val]),\n",
    "                            init_risky_asset_vals))"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "colab_type": "text",
    "id": "hZQgn_CTPQQE"
   },
   "source": [
    "### Make the initial portfolio "
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 19,
   "metadata": {
    "colab": {},
    "colab_type": "code",
    "id": "PyMLBotBPQQH"
   },
   "outputs": [],
   "source": [
    "# consider here two choices: equal or equally-weighted \n",
    "init_port_choice =  'equal' \n",
    "\n",
    "init_cash = 1000.0\n",
    "init_total_asset = np.sum(init_asset_vals)\n",
    "\n",
    "x_vals_init = np.zeros(num_assets)\n",
    "\n",
    "if init_port_choice == 'equal': \n",
    "    # hold equal amounts of cash in each asset\n",
    "    amount_per_asset = init_cash/num_assets\n",
    "    x_vals_init = amount_per_asset * np.ones(num_assets)\n",
    "\n",
    "elif init_port_choice == 'equally_weighted':\n",
    "    amount_per_asset = init_cash/init_total_asset\n",
    "    x_vals_init = amount_per_asset * init_asset_vals\n",
    "    "
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "colab_type": "text",
    "id": "Zkj8GdPDPQQP"
   },
   "source": [
    "### Make the target portfolio"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 45,
   "metadata": {
    "colab": {
     "base_uri": "https://localhost:8080/",
     "height": 34
    },
    "colab_type": "code",
    "id": "LyyKCaPGPQQS",
    "outputId": "bda0d8d5-b848-4c2b-c36b-3f2ac1a1bf78"
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "1100.0 1541.5835692311712\n"
     ]
    }
   ],
   "source": [
    "# Generate a target portfolio term structure by defining it as \n",
    "# the initial portfolio growing at some fixed and high rate\n",
    "target_portfolio = [init_cash]\n",
    "\n",
    "target_return = 0.15 \n",
    "coeff_target = 1.1 \n",
    "\n",
    "for i in range(1,num_steps):\n",
    "    target_portfolio.append(target_portfolio[i-1]*np.exp(dt * target_return) )\n",
    "    \n",
    "target_portfolio = coeff_target*np.array(target_portfolio)    \n",
    "print(target_portfolio[0], target_portfolio[-1])"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "colab_type": "text",
    "id": "2UMA8UwlPQQc"
   },
   "source": [
    "### Define model parameters"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 47,
   "metadata": {
    "colab": {
     "base_uri": "https://localhost:8080/",
     "height": 34
    },
    "colab_type": "code",
    "id": "5G_Y87m2PQQe",
    "outputId": "0cf27618-1f16-4676-9741-4e2c3ce8d466"
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "1133.1484530668263 3490.3429574618413\n"
     ]
    }
   ],
   "source": [
    "riskfree_rate = 0.02\n",
    "fee_bond = 0.05 # 0.01 # \n",
    "fee_stock = 0.05 # 0.05 # 20.0 # 0.1 # 1.0 # 100 # 1.0 # 0.5 \n",
    "\n",
    "all_fees = np.zeros(num_risky_assets + 1)\n",
    "all_fees[0] = fee_bond\n",
    "all_fees[1:] = fee_stock\n",
    "Omega_mat = np.diag(all_fees)\n",
    "\n",
    "# model parameters\n",
    "lambd = 0.001 \n",
    "Omega_mat = 15.5 * np.diag(all_fees) \n",
    "eta = 1.5 \n",
    "\n",
    "beta = 100.0\n",
    "gamma = 0.95 \n",
    "\n",
    "exp_returns = expected_risky_returns\n",
    "\n",
    "Sigma_r = cov_mat_r\n",
    "\n",
    "# Generate the benchmark target portfolio by growing the initial portfolio value at rate eta\n",
    "target_return =  0.5\n",
    "benchmark_portf = [ init_cash   * np.exp(dt * target_return)]\n",
    "\n",
    "rho = 0.4 \n",
    "\n",
    "for i in range(1,num_steps):\n",
    "    benchmark_portf.append(benchmark_portf[i-1]*np.exp(dt * target_return) )\n",
    "    \n",
    "print(benchmark_portf[0], benchmark_portf[-1])"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "colab_type": "text",
    "id": "YT6003xKPQQm"
   },
   "source": [
    "### Simulate portfolio data\n",
    "\n",
    "Produce a list of trajectories, where each trajectory is a list made of state-action pairs"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 22,
   "metadata": {
    "colab": {},
    "colab_type": "code",
    "id": "So89W92zPLMq"
   },
   "outputs": [],
   "source": [
    "lambd = 0.001 \n",
    "omega = 1.0 \n",
    "beta = 1000.0\n",
    "eta = 1.5 # 1.3 # 1.5 # 1.2\n",
    "rho = 0.4"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 23,
   "metadata": {
    "colab": {},
    "colab_type": "code",
    "id": "lCzgn95o8wUD"
   },
   "outputs": [],
   "source": [
    "reward_params=[lambd, omega, eta, rho]"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 26,
   "metadata": {
    "colab": {},
    "colab_type": "code",
    "id": "58NX65KOPQRD"
   },
   "outputs": [],
   "source": [
    "# Create a G-learner\n",
    "G_learner = G_learning_portfolio_opt(num_steps,\n",
    "                 reward_params,  \n",
    "                 beta,                \n",
    "                 benchmark_portf,\n",
    "                 gamma, \n",
    "                 num_risky_assets,\n",
    "                 riskfree_rate,\n",
    "                 expected_risky_returns, # array of shape num_steps x num_stocks\n",
    "                 Sigma_r,     # covariance matrix of returns of risky matrix                    \n",
    "                 x_vals_init, # array of initial values of len (num_stocks+1)\n",
    "                 use_for_WM = True) # use for wealth management tasks"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 25,
   "metadata": {
    "colab": {
     "base_uri": "https://localhost:8080/",
     "height": 34
    },
    "colab_type": "code",
    "id": "mhy6IPKs7nRq",
    "outputId": "78fbc7d6-2869-48a9-f754-c9ed7c5127c6"
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Doing G-learning, it may take a few seconds...\n"
     ]
    }
   ],
   "source": [
    "G_learner.reset_prior_policy()\n",
    "error_tol=1.e-8 \n",
    "max_iter_RL = 200\n",
    "G_learner.G_learning(error_tol, max_iter_RL)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 27,
   "metadata": {
    "colab": {
     "base_uri": "https://localhost:8080/",
     "height": 34
    },
    "colab_type": "code",
    "id": "ZJbI_k9mPQQo",
    "outputId": "45fc8ba1-fb6d-450b-8502-b96ffa56419a"
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Done simulating trajectories in 14.896530 sec\n"
     ]
    }
   ],
   "source": [
    "num_sim = 1000\n",
    "trajs = []\n",
    "np.random.seed(0)\n",
    "torch.manual_seed(0)\n",
    "t_0 = time.time()\n",
    "\n",
    "\n",
    "x_vals = [x_vals_init]\n",
    "returns_all = []\n",
    "for n in range(num_sim):\n",
    "    this_traj = []\n",
    "    x_t = x_vals_init[:]\n",
    "    returns_array = []\n",
    "    for t in range(0,num_steps):\n",
    "        mu_t = G_learner.u_bar_prior[t,:] + G_learner.v_bar_prior[t,:].mv(torch.tensor(x_t))\n",
    "        u_t = np.random.multivariate_normal(mu_t.detach().numpy(), G_learner.Sigma_prior[t,:].detach().numpy())\n",
    "        # compute new values of x_t\n",
    "\n",
    "        x_next = x_t +u_t\n",
    "        # grow this with random return\n",
    "        \n",
    "        idiosync_vol =  0.05 # vol_market     \n",
    "        rand_norm = np.random.randn(num_risky_assets)\n",
    "        \n",
    "        # asset returns are simulated from a one-factor model\n",
    "        risky_asset_returns = (expected_risky_returns[t,:] + beta_vals * (returns_market[t] - mu_market * dt) \n",
    "                         + idiosync_vol * np.sqrt(1 - beta_vals**2) * np.sqrt(dt) * rand_norm)\n",
    "        \n",
    "        returns = np.hstack((riskfree_rate*dt, risky_asset_returns))\n",
    "        \n",
    "        x_next = (1+returns)*x_next\n",
    "        port_returns=(x_next.sum() -x_t.sum() -np.sum(u_t) - 0.015*np.abs(u_t).sum())/x_t.sum()\n",
    "        \n",
    "        this_traj.append((x_t, u_t))\n",
    "        \n",
    "        # rename\n",
    "        x_t = x_next\n",
    "        returns_array.append(port_returns) \n",
    "    # end the loop over time steps\n",
    "    trajs.append(this_traj)\n",
    "    returns_all.append(returns_array)\n",
    "\n",
    "print('Done simulating trajectories in %f sec'% (time.time() - t_0))        "
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "colab_type": "text",
    "id": "bpnem5H5vZ5H"
   },
   "source": [
    "### Calculate performance of G-learner (Diagnostics only)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 28,
   "metadata": {
    "colab": {},
    "colab_type": "code",
    "id": "4Z731JGirsdV"
   },
   "outputs": [],
   "source": [
    "returns_all_G = returns_all"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 29,
   "metadata": {
    "colab": {
     "base_uri": "https://localhost:8080/",
     "height": 34
    },
    "colab_type": "code",
    "id": "UIYUAIWvO04X",
    "outputId": "7e9f5055-b670-4a22-c6cf-d8082c03c9c6"
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "-0.08632798115042445\n"
     ]
    }
   ],
   "source": [
    "SR_G = 0\n",
    "for i in range(num_sim):\n",
    "    SR_G += (np.mean(returns_all_G[i])-riskfree_rate*dt)/np.std(returns_all_G[i])\n",
    "\n",
    "SR_G/=num_sim\n",
    "print(SR_G)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 31,
   "metadata": {
    "colab": {},
    "colab_type": "code",
    "id": "TT9dIJP_t0K_"
   },
   "outputs": [],
   "source": [
    "r_G = np.array([0]*num_steps, dtype='float64')\n",
    "for n in range(num_steps):\n",
    "    for i in range(num_sim):\n",
    "        r_G[n]+=returns_all_G[i][n]\n",
    "    r_G[n]/=num_sim\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 33,
   "metadata": {
    "colab": {
     "base_uri": "https://localhost:8080/",
     "height": 296
    },
    "colab_type": "code",
    "id": "KAdjSXoHui7R",
    "outputId": "9f5ea1f7-38b7-4aff-ef5a-e9ef9788651d"
   },
   "outputs": [
    {
     "data": {
      "text/plain": [
       "Text(0, 0.5, 'Sample Mean Returns')"
      ]
     },
     "execution_count": 33,
     "metadata": {},
     "output_type": "execute_result"
    },
    {
     "data": {
      "image/png": "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\n",
      "text/plain": [
       "<Figure size 432x288 with 1 Axes>"
      ]
     },
     "metadata": {
      "needs_background": "light"
     },
     "output_type": "display_data"
    }
   ],
   "source": [
    "plt.plot(r_G, label='G-learning (' + str(np.round(SR_G,3)) + ')', color='red')\n",
    "\n",
    "plt.xlabel('time (quarters)')\n",
    "plt.ylabel('Sample Mean Returns');"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 34,
   "metadata": {
    "colab": {},
    "colab_type": "code",
    "id": "aTWkeFsDPQQu"
   },
   "outputs": [],
   "source": [
    "np.save('State_act_trajs.npy', trajs)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 35,
   "metadata": {
    "colab": {
     "base_uri": "https://localhost:8080/",
     "height": 286
    },
    "colab_type": "code",
    "id": "SNtCvuFMPQQ0",
    "outputId": "2f12bdef-6454-4811-9d4b-5ee128b9f596"
   },
   "outputs": [],
   "source": [
    "#trajs = np.load('State_act_trajs.npy')"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "colab_type": "text",
    "id": "RysHrlg3yDyP"
   },
   "source": [
    "## Plot rewards and cash installments"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "We can plot the learng rewards and observe the required cash installements from the G-learner model "
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 40,
   "metadata": {
    "colab": {},
    "colab_type": "code",
    "id": "pcU6SnH5JsC0"
   },
   "outputs": [],
   "source": [
    "reward_params=[lambd, omega, eta, rho]\n",
    "G_learner = G_learning_portfolio_opt(num_steps,\n",
    "                                      reward_params,\n",
    "                                      beta,\n",
    "                                      benchmark_portf,\n",
    "                                      gamma,\n",
    "                                      num_risky_assets,\n",
    "                                      riskfree_rate,\n",
    "                                      expected_risky_returns,\n",
    "                                      Sigma_r,\n",
    "                                      x_vals_init,\n",
    "                                      use_for_WM=True)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 48,
   "metadata": {
    "colab": {
     "base_uri": "https://localhost:8080/",
     "height": 52
    },
    "colab_type": "code",
    "id": "JiAyHd94PQRI",
    "outputId": "5c91e69d-0356-46c0-9013-71056b894952"
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Doing G-learning, it may take a few seconds...\n",
      "Done in 10.610811 sec\n"
     ]
    }
   ],
   "source": [
    "err_tol = 1.e-10 \n",
    "max_iter = 500\n",
    "\n",
    "t_0 = time.time()\n",
    "G_learner.reset_prior_policy()\n",
    "G_learner.G_learning(err_tol=err_tol, max_iter=max_iter)\n",
    "num_trajs = len(trajs)\n",
    "\n",
    "data_xvals = torch.zeros(num_trajs,  num_steps, num_assets, dtype=torch.float64, requires_grad=False)\n",
    "data_uvals = torch.zeros(num_trajs,  num_steps, num_assets, dtype=torch.float64, requires_grad=False)\n",
    "num_trajs = len(trajs)       \n",
    "for n in range(num_trajs):\n",
    "        for t in range(num_steps):\n",
    "            data_xvals[n,t,:] = torch.tensor(trajs[n][t][0],dtype=torch.float64)\n",
    "            data_uvals[n,t,:] = torch.tensor(trajs[n][t][1],dtype=torch.float64)\n",
    "\n",
    "realized_rewards = torch.zeros(num_trajs, num_steps, dtype=torch.float64, requires_grad=False)\n",
    "realized_cum_rewards = torch.zeros(num_trajs, dtype=torch.float64, requires_grad=False)\n",
    "\n",
    "realized_G_fun = torch.zeros(num_trajs, num_steps, dtype=torch.float64, requires_grad=False)\n",
    "realized_F_fun  = torch.zeros(num_trajs,  num_steps, dtype=torch.float64, requires_grad=False)\n",
    "\n",
    "realized_G_fun_cum = torch.zeros(num_trajs, dtype=torch.float64, requires_grad=False)  \n",
    "realized_F_fun_cum = torch.zeros(num_trajs, dtype=torch.float64, requires_grad=False)         \n",
    "\n",
    "# compute the rewards and realized values of G- and F-functions from \n",
    "# all trajectories\n",
    "for n in range(num_trajs):\n",
    "    for t in range(num_steps):\n",
    "                \n",
    "                realized_rewards[n,t] = G_learner.compute_reward_on_traj(t,\n",
    "                                data_xvals[n,t,:], data_uvals[n,t,:])\n",
    "\n",
    "                realized_G_fun[n,t] = G_learner.compute_G_fun_on_traj(t,\n",
    "                                data_xvals[n,t,:], data_uvals[n,t,:])\n",
    "\n",
    "                realized_F_fun[n,t] = G_learner.compute_F_fun_on_traj(t,\n",
    "                                data_xvals[n,t,:])\n",
    "                 \n",
    "    realized_cum_rewards[n] = realized_rewards[n,:].sum()\n",
    "    realized_G_fun_cum[n] = realized_G_fun[n,:].sum()\n",
    "    realized_F_fun_cum[n] = realized_F_fun[n,:].sum()\n",
    "\n",
    "print('Done in %f sec'% (time.time() - t_0))\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 38,
   "metadata": {
    "colab": {
     "base_uri": "https://localhost:8080/",
     "height": 295
    },
    "colab_type": "code",
    "id": "txOKhgy7PQRm",
    "outputId": "6bc8c687-3ac3-4aaf-b06a-c5ef4d89df77"
   },
   "outputs": [
    {
     "data": {
      "image/png": "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cBEZiOcmJl29diInk/NqlLHANqc9Npf23F53LDAWID4+3q7pYUwBSE5OJjw8nLi4OETyvBitKQRUlb1795KcnEyNGjXOeT2fDSWJSA88Fzu7zrlCZLbpQH/x3J6xBp6dzIucq1imiEhb8fyvHAB84av6jDE5paWlUb58eQuFIkREKF++/Hn3An15HsObQEngO+c/2gJVvV9VV4rIJDxXqswAHnCuMAkwmJOHq87i5H4JY8xFYKFQ9FzIv6nPgkFVa59l3r+Af+XSngA09lVN3hI37ePXdXupWymc+pXCqVq2FAEB9kthjDGF/sznC5WwcT+vfvf7ieelSgRSp2I49SuGU6/SyUdUWEkXqzTGnI877riDnj170q9fP+655x4eeeQRGjZseMHb27hxIz179iQpKakAqywYYWFhpKam+mTbxTYY7utci9vaVuf3nSms3ZHCWufn7NU7+Szh5NG0UWElqFcpnLoVPT2LepUiqFsxjFIliu1HZ4zPnbgpfcCF7wYdN25cAVZ0ZpmZmQQGnustxS9MRkYGQUEX7zunWH+7lS4ZRItqZWlRrWyO9t0px7zC4hBrd6Tw6aItHE337AoRgWrlSnmFhednXPnSBAXadQmNuRAbN27kqquu4rLLLmP+/PlMmzaNtWvX8ve//51jx45Rq1Ytxo8fT1hYGM8//zxffvklR48epX379rz99tunjaV36dKFESNGsG3bNp599lkAjh49yvHjx9mwYQOJiYk88sgjpKamEhUVxYQJE6hcuTKJiYncddddlCpVio4dO+ZWKj/99BPPPfcclStX5rfffmPVqlV89NFHjBo1iuPHj3PJJZcwevRopkyZwoIFC3jttdcYOXIkI0eO5M8//2T9+vUMHDiQuXPnnvG9dOnShfbt2zNv3jyuu+46+vbtyy233EJGRgY9epy8KMT27du56aabOHToEBkZGYwZM4ZLL700X/8WxToYziQ6vCTR4SXpWOfkvdSzspTN+46c6Fms3ZHCmh2H+H71TrKcA2ZLBAZQq0LYibDIDoxKESG2U88UKs99uZJV2w4V6DYbVong79c2Ousya9euZfz48YwePZo9e/bwwgsvMHv2bEqXLs3LL7/Ma6+9xrPPPsvQoUNPfNnffvvtzJgxg2uvvTbXbV533XVcd911ANx444107tyZ9PR0hg0bxhdffEF0dDSf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\n",
      "text/plain": [
       "<Figure size 432x288 with 1 Axes>"
      ]
     },
     "metadata": {
      "needs_background": "light"
     },
     "output_type": "display_data"
    }
   ],
   "source": [
    "sim_idx =  999\n",
    "\n",
    "realized_rewards = realized_rewards[sim_idx,:].detach().numpy()\n",
    "plt.plot(realized_rewards,label='realized rewards')\n",
    "plt.legend()\n",
    "plt.xlabel('Time Steps')\n",
    "plt.title('Realized rewards');"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 49,
   "metadata": {
    "colab": {
     "base_uri": "https://localhost:8080/",
     "height": 295
    },
    "colab_type": "code",
    "id": "jUeIUzmMPQRU",
    "outputId": "8afb4655-b13e-410a-89d6-bcfe26675163"
   },
   "outputs": [
    {
     "data": {
      "image/png": "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LWPevHncdddduamVQxk/fjw33ngjAwcOpGPHjrlpELKysrj22mtJTEykZ8+euSkjZs6cyZgxYxg5ciTDhg07Zj+GDRtGWloaSUlJLFmyhLVr19KvXz+6d+/O6NGj2bdvX6HHGVwqjMTERLp168Y999xzAr9+mDh40OWT/+1v3RutTZq4WPxLLzmP/bHHYPVqdzGYOxduvdU9hDXBN/JTUOrNivQ5bmrl225THTy4dD+33XbMNkPZsmWLioguX75cVVV37dqlAwcO1IyMDFVVffTRR/WPf/yjqgZT/qqqXnXVVTpv3jxVVR03blxuuuN3h0IAACAASURBVOPQVMMBxowZo3//+9+LTL+cmJioixYtUlXVO++8M0/q4AD50zEfOnRIDx8+rKqqGzdu1MDxXbhwodasWVN//PFHVVVdtWqV9ujRQzMzM/XgwYN62mmn6eOPP66qhaciDt2n/IwbN07PO+889fl8unHjRm3ZsqUePnxYn3jiCR0/fryqqn7//ffaqlUrPXz4sM6YMUNbtmyZe/zy70f+VMmhx+IPf/iD3ub9hgUd519++UVbtWqlaWlpmp2drUOHDtV33nmnQLuLQ5mkVj50SHXBAtV773WphaOjXarhuDjVs85Sffhh1c8+U/XSextGfiiN1MpGkDZt2uTmeFmxYgXr1q3LTfN79OhR+vfvD8DChQt57LHHyMzMZO/evSQkJDBy5Mgi237ssceoUaMGN998M99++22B6ZcPHDjA/v37c/PqXH311XzwwQfHtTs7O5spU6awdu1aoqOj2bhxY+6yvn375t5dLF26lIsvvjg3jXLA5pNJRXzZZZcRFRVFhw4daN++PevXr2fp0qW5A6F07tyZNm3a5Np07rnn0rBhw+O2m/9YjBs3Lo99+Vm1ahVDhgyhiTfwxpVXXsnixYsZNWpUsfajTDhyxKU1CIRrli+H7GyIiYHTT4f77nPhmv79rZ+8cVJUftEvYISl8iA0xbCqcu655zJr1qw8dbKyspg8eTKrV6+mVatWTJ069ZiUv/n5+OOPmT17NosXL85tu6D0y/v37y92+uRQnnrqKZo1a8ZXX32F3+/Pk6s//z4VxMmkIi5uGueC7ClNitpmuaEKX37pskwuXOheijp82IVjeveG2293In/mmW5YQMMoJSymXwr069ePzz77LDeNcGZmJhs3bswV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\n",
      "text/plain": [
       "<Figure size 432x288 with 1 Axes>"
      ]
     },
     "metadata": {
      "needs_background": "light"
     },
     "output_type": "display_data"
    }
   ],
   "source": [
    "G_learner.project_cash_injections()\n",
    "\n",
    "eta_ = G_learner.eta.detach().numpy()\n",
    "realized_target_portf = eta_ * G_learner.expected_portf_val.numpy()\n",
    "\n",
    "plt.plot(G_learner.expected_c_t, label='optimal cash installments')\n",
    "plt.plot(G_learner.expected_portf_val, label='expected portfolio value')\n",
    "plt.plot(realized_target_portf,label='realized target portfolio',color='r')\n",
    "\n",
    "plt.legend()\n",
    "plt.xlabel('Time Steps')\n",
    "plt.title('Optimal cash installment and portfolio value');"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Show the histogram of cumulative rewards"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 0,
   "metadata": {
    "colab": {
     "base_uri": "https://localhost:8080/",
     "height": 439
    },
    "colab_type": "code",
    "id": "LDVVOMCLNvM1",
    "outputId": "4d64768d-98f0-4c1b-d597-179aac815f6d"
   },
   "outputs": [
    {
     "data": {
      "text/plain": [
       "(array([  5.,   4.,  12.,  25.,  36.,  52.,  86., 100., 129., 112., 123.,\n",
       "        101.,  86.,  56.,  34.,  22.,  10.,   3.,   3.,   1.]),\n",
       " array([-315267.86864726, -314109.08054033, -312950.2924334 ,\n",
       "        -311791.50432647, -310632.71621953, -309473.9281126 ,\n",
       "        -308315.14000567, -307156.35189874, -305997.56379181,\n",
       "        -304838.77568488, -303679.98757795, -302521.19947102,\n",
       "        -301362.41136409, -300203.62325716, -299044.83515023,\n",
       "        -297886.04704329, -296727.25893636, -295568.47082943,\n",
       "        -294409.6827225 , -293250.89461557, -292092.10650864]),\n",
       " <a list of 20 Patch objects>)"
      ]
     },
     "execution_count": 31,
     "metadata": {
      "tags": []
     },
     "output_type": "execute_result"
    },
    {
     "data": {
      "image/png": "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\ngg==\n",
      "text/plain": [
       "<Figure size 432x288 with 1 Axes>"
      ]
     },
     "metadata": {
      "tags": []
     },
     "output_type": "display_data"
    }
   ],
   "source": [
    "plt.hist(realized_cum_rewards.detach().numpy(), bins=20);"
   ]
  }
 ],
 "metadata": {
  "colab": {
   "collapsed_sections": [],
   "include_colab_link": true,
   "machine_shape": "hm",
   "name": "Wealth_Management_GIRL.ipynb",
   "provenance": []
  },
  "kernelspec": {
   "display_name": "Python 3",
   "language": "python",
   "name": "python3"
  },
  "language_info": {
   "codemirror_mode": {
    "name": "ipython",
    "version": 3
   },
   "file_extension": ".py",
   "mimetype": "text/x-python",
   "name": "python",
   "nbconvert_exporter": "python",
   "pygments_lexer": "ipython3",
   "version": "3.6.10"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 1
}
